// Painters...................................................................

// Painters paint sprites with a paint(sprite, context) method. ImagePainters
// paint an image for their sprite.
/**
 * 封装好的精灵类
 */

/**
 *
 * @param imageUrl
 * @constructor
 */
var ImagePainter = function (imageUrl) {
    this.image = new Image;
    this.image.src = imageUrl;
};

ImagePainter.prototype = {
    image: undefined,
    paint: function (sprite, context) {
        if (this.image !== undefined) {
            if (!this.image.complete) {
                this.image.onload = function (e) {
                    sprite.width = this.width;
                    sprite.height = this.height;

                    context.drawImage(this,  // this is image
                        sprite.left, sprite.top,
                        sprite.width, sprite.height);
                };
            }
            else {
                context.drawImage(this.image, sprite.left, sprite.top,
                    sprite.width, sprite.height);
            }
        }
    }
};

SpriteSheetPainter = function (cells) {
    this.cells = cells;
};

SpriteSheetPainter.prototype = {
    cells: [],
    cellIndex: 0,

    advance: function () {
        if (this.cellIndex == this.cells.length - 1) {
            this.cellIndex = 0;
        }
        else {
            this.cellIndex++;
        }
    },

    paint: function (sprite, context) {
        var cell = this.cells[this.cellIndex];
        context.drawImage(spritesheet, cell.left, cell.top,
            cell.width, cell.height,
            sprite.left, sprite.top,
            cell.width, cell.height);
    }
};

// Sprite Animators...........................................................

// Sprite animators have an array of painters that they succesively apply
// to a sprite over a period of time. Animators can be started with 
// start(sprite, durationInMillis, restoreSprite)

var SpriteAnimator = function (painters, elapsedCallback) {
    this.painters = painters;
    if (elapsedCallback) {
        this.elapsedCallback = elapsedCallback;
    }
};

SpriteAnimator.prototype = {
    painters: [],
    duration: 1000,
    startTime: 0,
    index: 0,
    elapsedCallback: undefined,

    end: function (sprite, originalPainter) {
        sprite.animating = false;

        if (this.elapsedCallback) {
            this.elapsedCallback(sprite);
        }
        else {
            sprite.painter = originalPainter;
        }
    },

    start: function (sprite, duration) {
        var endTime = +new Date() + duration,
            period = duration / (this.painters.length),
            interval = undefined,
            animator = this, // for setInterval() function
            originalPainter = sprite.painter;

        this.index = 0;
        sprite.animating = true;
        sprite.painter = this.painters[this.index];

        interval = setInterval(function () {
            if (+new Date() < endTime) {
                sprite.painter = animator.painters[++animator.index];
            }
            else {
                animator.end(sprite, originalPainter);
                clearInterval(interval);
            }
        }, period);
    }
};

// Sprites....................................................................

// Sprites have a name, a painter, and an array of behaviors. Sprites can
// be updated, and painted.
//
// A sprite's painter paints the sprite: paint(sprite, context)
// A sprite's behavior executes: execute(sprite, context, time)

var Sprite = function (name, painter, behaviors) {
    if (name !== undefined)      this.name = name;
    if (painter !== undefined)   this.painter = painter;
    if (behaviors !== undefined) this.behaviors = behaviors;

    return this;
};

Sprite.prototype = {
    left: 0,//左上角的X坐标
    top: 0,//左上角的Y坐标
    width: 10,//精灵的宽
    height: 10,//精灵的高
    velocityX: 0,//精灵的水平速度
    velocityY: 0,//精灵的垂直速度
    visible: true,//表示精灵可见的boolean标志
    animating: false,//表示精灵是否在执行动画效果
    painter: undefined, // object with paint(sprite, context) 用于绘制此精灵的绘制器
    behaviors: [], // objects with execute(sprite, context, time) 一个包含精灵行为对象的数组，在精灵执行更新逻辑时，该数组中的各行为对象都会被运用于此精灵。
    paint: function (context) {
        if (this.painter !== undefined && this.visible) {
            this.painter.paint(this, context);
        }
    },

    update: function (context, time) {
        for (var i = this.behaviors.length; i > 0; --i) {
            this.behaviors[i - 1].execute(this, context, time);
        }
    }
};
